package com.ruoyi.aop;

import com.ruoyi.anno.Log;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.utils.SecurityUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;
import java.security.Security;
import java.util.Date;

@Aspect
@Component
public class LoggingAspect {

    // 定义切入点，匹配目标类的方法
    @Pointcut("execution(* com.ruoyi.aop.TargetClass.*(..))")
    public void targetMethods() {}

    // 前置通知，在目标方法执行前执行
    @Before("targetMethods()")
    public void beforeAdvice(JoinPoint joinPoint) {
        System.out.println("Before method: " + joinPoint.getSignature().getName());
    }

    // 后置通知，在目标方法执行后执行
    @After("targetMethods()")
    public void afterAdvice(JoinPoint joinPoint) {
        System.out.println("After method: " + joinPoint.getSignature().getName());
    }

    // 返回通知，在目标方法正常返回后执行
    @AfterReturning(pointcut = "targetMethods()", returning = "result")
    public void afterReturningAdvice(JoinPoint joinPoint, Object result) {
        System.out.println("Method " + joinPoint.getSignature().getName() + " returned: " + result);
    }

    // 异常通知，在目标方法抛出异常时执行
    @AfterThrowing(pointcut = "targetMethods()", throwing = "ex")
    public void afterThrowingAdvice(JoinPoint joinPoint, Exception ex) {
        System.out.println("Method " + joinPoint.getSignature().getName() + " threw exception: " + ex.getMessage());
    }

    // 环绕通知，在目标方法执行前后都可以进行增强
//    @Around("targetMethods()")
//    public Object aroundAdvice(org.aspectj.lang.ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
//        System.out.println("Around advice: Before method execution");
//        Object result = proceedingJoinPoint.proceed();
//        System.out.println("Around advice: After method execution");
//        return result;
//    }
    @Around("@annotation(com.ruoyi.anno.Log)")
    public Object aroundAdvice(org.aspectj.lang.ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        System.out.println("Around advice: Before method execution");
        System.out.println("aop执行 时间"+new Date());
        //获取被增强类和方法的信息
        Signature signature = proceedingJoinPoint.getSignature();
        MethodSignature methodSignature = (MethodSignature) signature;
        //获取被增强的方法对象
        Method method = methodSignature.getMethod();
        //从方法中解析注解
        if(method !=null ){
            Log log = method.getAnnotation(Log.class);
            System.out.println(log.name());
        }
        //调用原始目标方法运行
        Object result = proceedingJoinPoint.proceed();
        System.out.println("aop结束 时间"+new Date());
        System.out.println("Around advice: After method execution");
        return result;
    }

    @Around("@annotation(com.ruoyi.anno.UserCheck)")
    public Object aroundUserCheck(org.aspectj.lang.ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        // 获取被增强类和方法的信息
        MethodSignature methodSignature = (MethodSignature) proceedingJoinPoint.getSignature();
        // 获取被增强的方法对象
        Method method = methodSignature.getMethod();

        // 获取方法的参数值
        Object[] args = proceedingJoinPoint.getArgs();
        // 获取方法的参数名
        String[] parameterNames = methodSignature.getParameterNames();

        Long methodUserId = null;
        for (int i = 0; i < parameterNames.length; i++) {
            if ("userId".equals(parameterNames[i])) {
                methodUserId = (Long) args[i];
                break;
            }
        }

        if (methodUserId == null) {
            // 处理 userId 未找到的情况
            return AjaxResult.error("未找到 userId 参数");
        }

        // 获取当前用户的 userId
        Long currentUserId = SecurityUtils.getUserId();

        // 检查 userId 是否匹配
        if (!methodUserId.equals(currentUserId)) {
            return AjaxResult.error("操作记录的用户 ID 与当前用户不匹配");
        }

        // 调用原始目标方法运行
        Object result = proceedingJoinPoint.proceed();
        System.out.println("aop 结束 时间" + new Date());
        System.out.println("Around advice: After method execution");
        return result;
    }
}
